//
// Created by Yucheng on 6/3/17.
//

#ifndef DIYLANE_LOGGER_H
#define DIYLANE_LOGGER_H

#include <iostream>

#ifdef WIN32
#define __func__ __FUNCTION__
#endif

// This define must be before including "cpplog.hpp"
#define LOG_LEVEL(level, logger) diylane::CustomLogMessage(__FILE__, __func__, __LINE__, (level), logger).getStream()
#include "cpplog.hpp"

namespace diylane
{
class CustomLogMessage : public cpplog::LogMessage
{
  public:
    CustomLogMessage(const char *file, const char *function,
                     unsigned int line, cpplog::loglevel_t logLevel,
                     cpplog::BaseLogger &outputLogger)
        : cpplog::LogMessage(file, line, logLevel, outputLogger, false),
          m_function(function)
    {
        InitLogMessage();
    }

    static const char *shortLogLevelName(cpplog::loglevel_t logLevel)
    {
        switch (logLevel)
        {
        case LL_TRACE:
            return "T";
        case LL_DEBUG:
            return "D";
        case LL_INFO:
            return "I";
        case LL_WARN:
            return "W";
        case LL_ERROR:
            return "E";
        case LL_FATAL:
            return "F";
        default:
            return "O";
        };
    }

  protected:
    virtual void InitLogMessage()
    {
        // Get the current time.
        ::time_t currTime;
        ::time(&currTime);
        ::tm timeInfo;
        cpplog::helpers::slocaltime(&timeInfo, &currTime);
        m_logData->stream
            << std::setfill('0')
            << std::setw(4) << (timeInfo.tm_year + 1900) << "-"
            << std::setw(2) << timeInfo.tm_mon << "-"
            << std::setw(2) << timeInfo.tm_mday << " "
            << std::setw(2) << timeInfo.tm_hour << ":"
            << std::setw(2) << timeInfo.tm_min << ":"
            << std::setw(2) << timeInfo.tm_sec
            << " ["
            << m_logData->fileName << ":"
            << m_function << ":"
            << m_logData->line
            << "]["
            << shortLogLevelName(m_logData->level)
            << "] ";
    }

  private:
    const char *m_function;
};

struct LogConfig
{
    std::streamoff max_size;
    std::string name;
};

struct Logger
{
    LogConfig config;
    cpplog::SizeRotateFileLogger *logger = NULL;

    static void SizeNameFunc(unsigned long logNumber, std::string &newFileName, void *context)
    {
        LogConfig *config = (LogConfig *)context;

        // Get the current time.
        ::time_t currTime;
        ::time(&currTime);
        ::tm timeInfo;
        cpplog::helpers::slocaltime(&timeInfo, &currTime);

        std::ostringstream fileName;
        fileName << config->name << "_";
        fileName << std::setfill('0');
        fileName << std::setw(4) << (timeInfo.tm_year + 1900)
                 << std::setw(2) << timeInfo.tm_mon
                 << std::setw(2) << timeInfo.tm_mday
                 << "_"
                 << std::setw(2) << timeInfo.tm_hour
                 << std::setw(2) << timeInfo.tm_min
                 << std::setw(2) << timeInfo.tm_sec
                 << "_"
                 << logNumber
                 << ".log";
        newFileName = fileName.str();
    }

    Logger(const char *_name = "diylane", std::streamoff _max_size = 100000)
    {
        config.name = _name;
        config.max_size = _max_size;
        logger = new cpplog::SizeRotateFileLogger(SizeNameFunc, &config, config.max_size);
    }

    ~Logger()
    {
        if (logger)
        {
            delete logger;
            logger = NULL;
        }
    }

    // singleton helper
    static Logger &singleton(void *uniquePtr = NULL)
    {
        static Logger *singletonPointer = NULL;
        if (uniquePtr)
            singletonPointer = (Logger *)uniquePtr;
        return *singletonPointer;
    }

    /**
     * the way to reference a single logger globally.
     */
    static cpplog::SizeRotateFileLogger &inst()
    {
        return *singleton().logger;
    }
};
}

#endif //DIYLANE_LOGGER_H
